package com.xf.system.utils.cloudApi.wx;

import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author xiefengf
 *
 * 微信小程序相关接口
 */
@Slf4j
@Service
public class WxService {

    private String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";

    private String qrCodeUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit";

    private String sessionKeyUrl = "https://api.weixin.qq.com/sns/jscode2session";

    private String phoneNumberUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";

    private String messageUrl = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send";

    private String appId = "xxxxxxx";

    private String appSecret = "xxxxxxx";

    private String templateId = "xxxxxxx";

    /**
     * 微信小程序Token的key
     */
    public static final String WX_ACCESS_TOKEN_KEY = "WX_ACCESS_TOKEN";

    /**
     * 微信小程序Token过期时间 - 600s
     */
    public static final Long WX_ACCESS_TOKEN_TTL = 600L;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 微信小程序推送消息给司机端
     * @param openId 司机openId
     * @param loadWarehouseId 装车仓库ID
     * @param loadWarehouseName 装车仓库名称
     * @param tips 提示信息
     * @param orderNo 订单号
     * @param carNo 车牌号
     * @param telephone 司机手机号
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/sendMessage.html
     */
    public void notifyDriverFinish(String openId,Long loadWarehouseId,String loadWarehouseName,String tips,String orderNo,String carNo,String telephone){
        // 1、发送内容
        HashMap<String, ValueContentVo> dataMap = new HashMap<>();
        dataMap.put("thing1",ValueContentVo.builder().value(loadWarehouseName).build());
        dataMap.put("thing2",ValueContentVo.builder().value(tips).build());
        dataMap.put("character_string3",ValueContentVo.builder().value(orderNo).build());
        dataMap.put("car_number4",ValueContentVo.builder().value(carNo).build());
        dataMap.put("phone_number6",ValueContentVo.builder().value(telephone).build());
        // 2、请求参数
        HashMap<String, Object> reqMap = new HashMap<>();
        // 2.1 接收人openId
        reqMap.put("touser", openId);
        // 2.2 模板id
        reqMap.put("template_id", templateId);
        // 2.3 跳转地址
        reqMap.put("page", "pages/auth/auth?"+loadWarehouseId+","+telephone);
        // 2.4 小程序状态 - developer为开发版；trial为体验版；formal为正式版；默认为正式版
        reqMap.put("miniprogram_state","formal");
        // 2.5 语言
        reqMap.put("lang", "zh_CN");
        // 2.6 发送内容
        reqMap.put("data", dataMap);
        // 3、获取access_token
        String accessToken = getAccessToken();
        // 4、发送请求
        String result;
        try {
            result = HttpRequest.post(messageUrl+"?access_token="+accessToken)
                    .body(JSONUtil.toJsonStr(reqMap))
                    .contentType("text/json")
                    .timeout(5000)
                    .execute().body();
            // 5、校验接口返回结果
            HashMap resultMap = JSONUtil.parseObj(result).toBean(HashMap.class);
            if(!resultMap.get("errcode").equals(0)){
                log.error("微信推送装车完成异常："+result+"司机手机号："+telephone);
                String errcode = resultMap.get("errcode").toString();
                // token可能失效，重新生成
                if("40001".equals(errcode)){
                    stringRedisTemplate.delete(WX_ACCESS_TOKEN_KEY);
                    String token = getAccessToken();
                    log.info("生成全新微信小程序Token："+token);
                }
                log.error("微信推送请求返回结果异常"+result+"司机手机号："+telephone);
            }
        } catch (HttpException e) {
            log.error("微信推送装车完成信息请求异常："+e.getMessage()+"司机手机号："+telephone);
        }
    }

    /**
     * 通过获取小程序的code获取session_key
     * @param code 小程序登录时获取的code
     * @return session_key + openid + unionid
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
     */
    public HashMap getCode2Session(String code){
        // 请求参数
        HashMap<String, Object> map = new HashMap<>();
        map.put("appid", appId);
        map.put("secret", appSecret);
        map.put("js_code", code);
        map.put("grant_type","authorization_code");
        // 发送请求
        String body = HttpRequest.get(sessionKeyUrl)
                .form(map)
                .timeout(5000)
                .execute().body();
        JSONObject jsonObject = JSONUtil.parseObj(body);
        // 校验接口返回的错误码
        Object errcode = jsonObject.get("errcode");
        if (errcode != null && !"0".equals(errcode.toString())){
            log.error("调用获取session_key接口失败，错误信息为：{}", jsonObject.get("errmsg").toString());
            throw new RuntimeException("调用微信小程序获取session_key接口失败！");
        }
        // 解析响应结果并封装 session_key + openid + unionid
        return jsonObject.toBean(HashMap.class);
    }

    /**
     * 通过获取小程序的code获取手机号
     * @param code 小程序登录时获取的code
     * @return 手机号
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html#%E8%B0%83%E7%94%A8%E6%96%B9%E5%BC%8F
     * https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html - 这里的Code不是登录Code
     */
    public String getUserPhoneNumber(String code){
        // 请求参数
        HashMap<String, String> map = new HashMap<>();
        map.put("code", code);
        // 获取access_token
        String accessToken = getAccessToken();
        // 发送请求
        String body = HttpRequest.post(phoneNumberUrl+"?access_token="+accessToken)
                .body(JSONUtil.toJsonStr(map))
                .timeout(5000)
                .execute().body();
        JSONObject jsonObject = JSONUtil.parseObj(body);
        // 校验接口返回的错误码
        Object errcode = jsonObject.get("errcode");
        if (errcode != null && !"0".equals(errcode.toString())){
            log.error("调用获取手机号接口失败，错误信息为：{}", jsonObject.get("errmsg").toString());
            throw new RuntimeException("调用微信小程序获取手机号接口失败！");
        }
        // 解析响应结果
        String phoneInfo = String.valueOf(jsonObject.get("phone_info"));
        HashMap hashMap = JSONUtil.parseObj(phoneInfo).toBean(HashMap.class);
        Object phoneNumber = hashMap.get("phoneNumber");
        if (phoneNumber == null){
            log.error("调用获取手机号接口失败，错误信息为：{}", jsonObject.get("errmsg").toString());
            throw new RuntimeException("调用微信小程序获取手机号接口失败！");
        }
        return String.valueOf(phoneNumber);
    }

    /**
     * 获取小程序二维码
     * @param id 装车仓库ID
     * @return Base64格式的图片
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html
     */
    public String getQrCode(Long id,String telephone) {
        try {
            // 获取access_token
            String accessToken = getAccessToken();
            // 请求参数
            HashMap<String, Object> map = new HashMap<>();
            // 要打开的小程序版本。正式版为 "release"，体验版为 "trial"，开发版为 "develop"。默认是正式版。
            map.put("env_version", "release");
            map.put("check_path", false);
            map.put("page", "pages/auth/auth");
            map.put("scene", id+","+telephone);
            // 微信接口请求
            HttpResponse execute = HttpRequest.post(qrCodeUrl+"?access_token="+accessToken)
                    .body(JSONUtil.toJsonStr(map))
                    .timeout(5000)
                    .execute();
            if (!execute.isOk()){
                throw new RuntimeException("微信小程序二维码接口调用失败！");
            }
            // 图片二进制
            byte[] imageBytes = execute.bodyBytes();
            // 校验返回的结果是否是图片
            InputStream in = new ByteArrayInputStream(imageBytes);
            BufferedImage image = ImageIO.read(in);
            if (image == null) {
                // 校验失败，抛出异常
                throw new RuntimeException("获取小程序二维码失败");
            }
            // 转为图片base64返回
            return Base64.getEncoder().encodeToString(imageBytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取微信小程序的access_token
     * @return access_token
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
     */
    private String getAccessToken() {
        try {
            // 先从Redis中获取access_token
            String accessToken = stringRedisTemplate.opsForValue().get(WX_ACCESS_TOKEN_KEY);
            if (accessToken != null) {
                // 不为空，还没过期，直接返回
                return accessToken;
            }
            // 请求参数
            HashMap<String, Object> formData = new HashMap<>();
            formData.put("grant_type","client_credential");
            formData.put("appid",appId);
            formData.put("secret",appSecret);
            HttpResponse execute = HttpRequest.get(tokenUrl)
                    .form(formData)
                    .timeout(5000)
                    .execute();
            if (!execute.isOk()) {
                throw new RuntimeException("获取微信小程序Token失败");
            }
            // 获取返回数据
            String body = execute.body();
            String token = JSONUtil.toBean(body, HashMap.class).get("access_token").toString();
            // 存入Redis
            stringRedisTemplate.opsForValue().set(WX_ACCESS_TOKEN_KEY, token,WX_ACCESS_TOKEN_TTL,TimeUnit.SECONDS);
            // 返回数据
            return token;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 微信小程序 - 通知内容
     */
    @Data
    @Builder
    public static class ValueContentVo {

        private String value;

    }


}
